﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Timers;

namespace WindowsStateService.Monitor
{
    public class NetMonitor   
    {
        private Dictionary<string, Tuple<long, long>> net_to_last_rx_bytes_dic;
        private Dictionary<string, OperationalStatus> net_status_dic;
        private Dictionary<string, System.Timers.Timer> all_data_timers;
        private Dictionary<string, System.Timers.Timer> all_status_timers;

        public event EventHandler<NetEventArgs> NetChangedEventHandler;

        public NetMonitor()
        {
            all_data_timers = new Dictionary<string, System.Timers.Timer>();
            all_status_timers = new Dictionary<string, System.Timers.Timer>();
            net_to_last_rx_bytes_dic = new Dictionary<string, Tuple<long, long>>();
            net_status_dic = new Dictionary<string, OperationalStatus>();
        }

        public void Initialize()
        {
          //  Debugger.Launch();
            var net_interfaces = GetEthernetInterface();
            if (net_interfaces.Count == 0)
            {
                ServiceLogManager.Instance.Error($"未查询到有本地以太网连接。");
            }
            else
            {
                string msg = $"查询到以下网络：" ;
                foreach (var net in net_interfaces)
                {
                    msg += (Environment.NewLine + net.Name);
                }
                ServiceLogManager.Instance.Info(msg);
            }
            RecordNetStatsDic(net_interfaces);
        }

        private void RecordNetStatsDic(IList<NetworkInterface> net_interfaces)
        {
            //Debugger.Launch();
            CommonParas.Instance.NetIds.Clear();
            foreach (NetworkInterface nf in net_interfaces)
            {
                net_status_dic.Add(nf.Id, nf.OperationalStatus);
                CommonParas.Instance.NetIds.Add(new Data.NetInfo() { Name = nf.Name, Id = nf.Id});
                IPv4InterfaceStatistics statistics = nf.GetIPv4Statistics();
                Tuple<long, long> rx = new Tuple<long, long>(statistics.BytesSent, statistics.BytesReceived);
                net_to_last_rx_bytes_dic.Add(nf.Id, rx);
            }
            using (FileStream fs = new FileStream(CommonPath.MonitorConfigFileName, FileMode.Truncate, FileAccess.Write, FileShare.ReadWrite))
            {
                using (StreamWriter writer = new StreamWriter(fs))
                {
                    string data_str = JsonConvert.SerializeObject(CommonParas.Instance);
                    writer.WriteLine(data_str);
                }
            }
        }

        public void DoMonitor()
        {
            if (all_data_timers.Count == 0)
            {
                DoNetMonite();
            }
            else
            {
                foreach(var timer in all_data_timers.Values)
                {
                    if (!timer.Enabled)
                        timer.Start();
                }
            }
            if(all_status_timers.Count == 0)
            {
                DoStatusMonite();
            }
            else
            {
                foreach (var timer in all_status_timers.Values)
                {
                    if (!timer.Enabled)
                        timer.Start();
                }
            }
            
        }

        private void DoNetMonite()
        {
            RecordOne();
            foreach (NetworkInterface nf in GetEthernetInterface())
            {
                string id = nf.Id;
                string name = nf.Name;
                System.Timers.Timer t = new System.Timers.Timer();
                all_data_timers.Add(nf.Id, t);
                t.Interval = 240 * 1000;
                t.Elapsed += (s, e)=> {
                    DoNetWork(s, e , id, name);
                };
                t.Start();
            }
        }

        private void DoStatusMonite()
        {
            foreach (NetworkInterface nf in GetEthernetInterface())
            {
                string id = nf.Id;
                string name = nf.Name;
                System.Timers.Timer t = new System.Timers.Timer();
                all_status_timers.Add(nf.Id, t);
                t.Interval = 15 * 1000;
                t.Elapsed += (s, e) => {
                    DoNetStatusWork(s, e, id, name);
                };
                t.Start();
            }
        }

        private void RecordOne()
        {
            foreach (NetworkInterface nf in GetEthernetInterface())
            {
                string id = nf.Id;
                DoNetWork(null, null, id, nf.Name);
            }
        }

        public void Stop()
        {
            foreach (var item in all_data_timers.Values)
            {
                item.Stop();
            }
            foreach (var item in all_status_timers.Values)
            {
                item.Stop();
            }
        }

        private void DoNetWork(object sender, ElapsedEventArgs arg, string id, string name)
        {
            try
            {
                System.Timers.Timer timer = sender as System.Timers.Timer;
                NetworkInterface ethernetInterface = GetInterfaceById(id);
                if (ethernetInterface == null)
                {
                    // RaiseNetChangedEventHandler(new NetEventArgs() { EventType = NetEventType.Error, ErrorMsg = $"以太网接口无效！id={id}, name={name}" });
                    //if (timer != null)
                    //{
                    //    timer.Stop();
                    //}
                    //  RaiseNetChangedEventHandler(new NetEventArgs() { EventType = NetEventType.Error, ErrorMsg = $"定时器已停止！id={id}, name={name}" });
                    RaiseNetChangedEventHandler(new NetEventArgs(NetEventType.Normal, id, name) { ReceivedBytes = 0, SendBytes = 0 });
                    return;
                }

                var statistic = ethernetInterface.GetIPv4Statistics();
                Tuple<long, long> current_bytes = net_to_last_rx_bytes_dic[id];

                long bytesReceivedInSecond = Math.Abs(statistic.BytesReceived - current_bytes.Item2);
                long bytesSendInSecond = Math.Abs(statistic.BytesSent - current_bytes.Item1);
                net_to_last_rx_bytes_dic[id] = new Tuple<long, long>(statistic.BytesSent, statistic.BytesReceived);
                RaiseNetChangedEventHandler(new NetEventArgs(NetEventType.Normal, ethernetInterface) { ReceivedBytes = (long)bytesReceivedInSecond, SendBytes = (long)bytesSendInSecond });
            }
            catch (Exception ex)
            {
                ServiceLogManager.Instance.Error(ex.StackTrace);
            }
        }

        private void DoNetStatusWork(object sender, ElapsedEventArgs arg, string id, string name)
        {
            try
            {
               //Debugger.Launch();
                System.Timers.Timer timer = sender as System.Timers.Timer;
                NetworkInterface ethernetInterface = GetInterfaceById(id);
                if (ethernetInterface == null)
                {
                    if (net_status_dic.ContainsKey(id))
                    {
                        RaiseNetChangedEventHandler(new NetEventArgs() { EventType = NetEventType.Error, ErrorMsg = $"DoNetStatusWork:以太网接口可能被禁用。id={id}, name={name}" });
                        net_status_dic.Remove(id);
                    }
                    return;
                }
                else
                {
                    if(!net_status_dic.ContainsKey(id))
                    {
                        net_status_dic.Add(id, ethernetInterface.OperationalStatus);
                        RaiseNetChangedEventHandler(new NetEventArgs() { EventType = NetEventType.NetInterfaceStatusChanged, Status = net_status_dic[id], NetName = ethernetInterface.Name, NetId = ethernetInterface.Id });
                        return;
                    }
                }
                OperationalStatus current_status = net_status_dic[id];
                if (current_status != ethernetInterface.OperationalStatus)
                {
                    net_status_dic[id] = ethernetInterface.OperationalStatus;
                    if (timer.Enabled)
                    {
                        RaiseNetChangedEventHandler(new NetEventArgs() { EventType = NetEventType.NetInterfaceStatusChanged, Status = net_status_dic[id], NetName = ethernetInterface.Name, NetId = ethernetInterface.Id });
                    }
                }
            }catch(Exception ex)
            {
                ServiceLogManager.Instance.Error(ex.StackTrace);
            }
        }

        private void RaiseNetChangedEventHandler(NetEventArgs arg)
        {
            try
            {
                if(NetChangedEventHandler != null)
                {
                    NetChangedEventHandler(this, arg);
                }
            }
            catch
            {
                //do something
            }
        }

        private IList<NetworkInterface> GetEthernetInterface()
        {
            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
            IList<NetworkInterface> res = new List<NetworkInterface>();
            foreach(var ni in networkInterfaces)
            {
                if(ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet || ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
                {
                    res.Add(ni);
                }
            }
            return res;
        }

        private NetworkInterface GetInterfaceById(string id)
        {
            return GetEthernetInterface().FirstOrDefault((net) => { return net.Id == id; });
        }

        //private void DoSleep(int time_second)
        //{
        //    int sleep_time = time_second;
        //    while ((!stop_sleep) && (sleep_time != 0))
        //    {
        //        Thread.Sleep(1000);
        //        sleep_time--;
        //    }
        //}
    }
    
    public enum NetEventType
    {
        NetInterfaceAdded = 0,
        NetInterfaceRemoved = 1,
        NetInterfaceStatusChanged = 2,
        Normal = 3,
        Error = 4
    }

    public class NetEventArgs: EventArgs
    {
        public NetEventType EventType { get; set; }
        public string NetId { get; set; }

        public string NetName { get; set; }

        public string NetDescription { get; set; }

        public OperationalStatus Status { get; set; }

        public long ReceivedBytes { get;set; }
        public long SendBytes { get;set; }

        public string ErrorMsg { get;set; } 

        public NetEventArgs() { }

        public NetEventArgs(NetEventType _type, NetworkInterface inter)
        {
            EventType = _type;
            NetId = inter.Id;
            NetName = inter.Name;
            NetDescription = inter.Description;
            Status = inter.OperationalStatus;
        }

        public NetEventArgs(NetEventType _type, string id, string name)
        {
            EventType = _type;
            NetId = id;
            NetName = name;
        }
    }

}
